/*

Miranda IM: the free IM client for Microsoft* Windows*

Copyright 2000-2003 Miranda ICQ/IM project, 
all portions of this codebase are copyrighted to the people 
listed in contributors.txt.

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
aLONG with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

#include "commonheaders.h"
#include "exportModules.h"

/***********************************************************************************************************
 * typedefs
 ***********************************************************************************************************/

typedef struct {
	HANDLE		hContact;
	LPENUMLIST	pModules;
} EXPORTDATA, *LPEXPORTDATA;

/***********************************************************************************************************
 * modules stuff
 ***********************************************************************************************************/

/**
 * name:	ExportTree_AppendModuleList
 * desc:	according to the checked list items create the module list for exporting
 * param:	hTree		- handle to the window of the treeview
 *			hParent		- parent tree item for the item to add
 *			pModules	- module list to fill
 * return:	nothing
 **/
void ExportTree_AppendModuleList(HWND hTree, HTREEITEM hParent, LPENUMLIST pModules)
{
	TVITEMA tvi;

	// add all checked modules
	if(tvi.hItem = TreeView_GetChild(hTree, hParent)) {
		CHAR szModule[MAXSETTING];
		LPENUMLISTITEM pModule;

		// add optional items
		tvi.mask = TVIF_STATE|TVIF_TEXT;
		tvi.stateMask = TVIS_STATEIMAGEMASK;
		tvi.pszText = szModule;
		tvi.cchTextMax = MAXSETTING;

		do {
			if(
				SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi) &&
				(
					tvi.state == INDEXTOSTATEIMAGEMASK(0) ||
					tvi.state == INDEXTOSTATEIMAGEMASK(2)
				) 
			  )
			{
				// create new module
				if(!(pModule = (LPENUMLISTITEM)malloc(sizeof(ENUMLISTITEM))))
					break;
				if(!(pModule->pszName = _strdup(tvi.pszText)))
					break;
				pModule->next = NULL;

				if(pModules->first) {
					pModules->last->next = pModule;
				}
				else {
					pModules->first = pModule;
				}
				pModules->last = pModule;
			}
		}
		while(tvi.hItem = TreeView_GetNextSibling(hTree, tvi.hItem));
	}
}

/**
 * name:	ExportTree_FindItem
 * desc:	find a item by its label
 * param:	hTree		- handle to the window of the treeview
 *			hParent		- parent tree item for the item to add
 *			pszText		- text to match the label against
 * return:	a handle to the found treeitem or NULL
 **/
HTREEITEM ExportTree_FindItem(HWND hTree, HTREEITEM hParent, LPSTR pszText)
{
   TVITEMA tvi;
   CHAR szBuf[128];

   if(!pszText || !*pszText) return NULL;

   tvi.mask = TVIF_TEXT;
   tvi.pszText = szBuf;
   tvi.cchTextMax = ARRAYSIZE(szBuf);
   
   for(tvi.hItem = TreeView_GetChild(hTree, hParent);
      tvi.hItem != NULL;
      tvi.hItem = TreeView_GetNextSibling(hTree, tvi.hItem))
   {
      if(SendMessageA(hTree, TVM_GETITEMA, NULL, (LPARAM)&tvi) && !mir_stricmp(tvi.pszText, pszText))
         return tvi.hItem;
   }
   return NULL;
}

/**
 * name:	ExportTree_AddItem
 * desc:	add an item to the tree view with given options
 * param:	hTree		- handle to the window of the treeview
 *			hParent		- parent tree item for the item to add
 *			pszDesc		- item label
 *			bUseImages	- icons are loaded
 *			bState		- 0-hide checkbox/1-unchecked/2-checked
 * return:	return handle to added treeitem 
 **/
HTREEITEM ExportTree_AddItem(HWND hTree, HTREEITEM hParent, LPSTR pszDesc, BOOLEAN bUseImages, BYTE bState)
{
	TVINSERTSTRUCTA	tvii;
	HTREEITEM hItem = NULL;

	tvii.hParent = hParent;
	tvii.hInsertAfter = TVI_SORT;
	tvii.itemex.mask = TVIF_TEXT;
	if(bUseImages) {
		tvii.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
		tvii.itemex.iImage = tvii.itemex.iSelectedImage = 1;
	}
	tvii.itemex.pszText = pszDesc;
	if(hItem = (HTREEITEM)SendMessageA(hTree, TVM_INSERTITEMA, NULL, (LPARAM)&tvii))
		TreeView_SetItemState(hTree, hItem, INDEXTOSTATEIMAGEMASK(bState), TVIS_STATEIMAGEMASK);
	return hItem;
}

/**
 * name:	export_GetModList_DlgProc
 * desc:	dialog procedure for a dialogbox, which lists modules for a specific contact
 * param:	hDlg		- handle to the window of the dialogbox
 *			uMsg		- message to handle
 *			wParam		- message specific parameter
 *			lParam		- message specific parameter
 * return:	message specific
 **/
INT CALLBACK export_GetModList_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	LPEXPORTDATA pDat = (LPEXPORTDATA)GetData(hDlg);

	switch(uMsg) {
	
		case WM_INITDIALOG:
		{
			HWND hTree;
			BOOLEAN bImagesLoaded = 0;

			// get tree handle and set treeview style
			if(!(hTree = GetDlgItem(hDlg, IDC_TREE))) break;
			SetWindowLong(hTree, GWL_STYLE, GetWindowLong(hTree, GWL_STYLE) | TVS_NOHSCROLL | TVS_CHECKBOXES);

			// init the datastructure
			if(!(pDat = (LPEXPORTDATA)malloc(sizeof(EXPORTDATA))))
				return FALSE;
			pDat->hContact = ((LPEXPORTDATA)lParam)->hContact;
			pDat->pModules = ((LPEXPORTDATA)lParam)->pModules;
			ZeroMemory(pDat->pModules, sizeof(ENUMLIST));
			SetData(hDlg, pDat);

			// set icons
			{
				HICON hIcon;
				HIMAGELIST hImages;
				OSVERSIONINFO osvi;
				const ICONCTRL idIcon[] = {
					{ ICO_DLG_EXPORT,	WM_SETICON,		NULL		},
					{ ICO_DLG_EXPORT,	STM_SETIMAGE,	ICO_DLGLOGO	},
					{ ICO_BTN_EXPORT,	BM_SETIMAGE,	IDOK		},
					{ ICO_BTN_CANCEL,	BM_SETIMAGE,	IDCANCEL	}
				};
				const INT numIconsToSet = DBGetByte(SET_ICONS_BUTTONS, 1) ? ARRAYSIZE(idIcon) : 2;
				IcoLib_SetCtrlIcons(hDlg, idIcon, numIconsToSet);

				// create imagelist for treeview
				osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
				GetVersionEx(&osvi);
				if((hImages = ImageList_Create(
						GetSystemMetrics(SM_CXSMICON),
						GetSystemMetrics(SM_CYSMICON),
						((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT && osvi.dwMajorVersion >= 5 && osvi.dwMinorVersion >= 1) ? ILC_COLOR32 : ILC_COLOR16)|ILC_MASK,
						0, 1)
					) != NULL)
				{
					SendMessage(hTree, TVM_SETIMAGELIST, TVSIL_NORMAL, (LPARAM)hImages);

					bImagesLoaded 
						= ((((hIcon = IcoLib_GetIcon(ICO_LST_MODULES)) != NULL && hIcon != ghEmptyIcon) && 0 == ImageList_AddIcon(hImages, hIcon))
						&& (((hIcon = IcoLib_GetIcon(ICO_LST_FOLDER)) != NULL && hIcon != ghEmptyIcon) && 1 == ImageList_AddIcon(hImages, hIcon)));
				}
			}
			// do the translation stuff
			{
				SendDlgItemMessage(hDlg, BTN_CHECK, BUTTONTRANSLATE, NULL, NULL);
				SendDlgItemMessage(hDlg, BTN_UNCHECK, BUTTONTRANSLATE, NULL, NULL);
				SendDlgItemMessage(hDlg, IDOK, BUTTONTRANSLATE, NULL, NULL);
				SendDlgItemMessage(hDlg, IDCANCEL, BUTTONTRANSLATE, NULL, NULL);
			}
			// Set the Window Title and description
			{
				LPCTSTR name = NULL; 
				TCHAR	oldTitle[MAXDATASIZE], 
						newTitle[MAXDATASIZE];
				switch((LONG_PTR)pDat->hContact) {
					case NULL:
						name = TranslateT("Owner");
						break;
					case INVALID_HANDLE_VALUE:
						name = TranslateT("All Contacts");
						break;
					default:
						name = DBGetContactDisplayName(pDat->hContact);
						break;
				}
				GetWindowText(hDlg, oldTitle, MAXSETTING);
				mir_sntprintf(newTitle, MAXDATASIZE - 1, _T("%s - %s"), name, oldTitle);
				SetWindowText(hDlg, newTitle);
			}

			{
				LPSTR pszBaseProto;
				TVINSERTSTRUCT	tviiT;
				ENUMLIST Modules;
				LPENUMLISTITEM pModule;
				HTREEITEM hItemEssential, hItemOptional;

				TreeView_SetIndent(hTree, 6);
				TreeView_SetItemHeight(hTree, 18);
				
				pszBaseProto 
					= (pDat->hContact && pDat->hContact != INVALID_HANDLE_VALUE)
					? (LPSTR)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)pDat->hContact, NULL)
					: NULL;

				// add items that are always exported
				tviiT.hParent = TVI_ROOT;
				tviiT.hInsertAfter = TVI_FIRST;
				tviiT.itemex.mask = TVIF_TEXT|TVIF_STATE;
				tviiT.itemex.pszText = TranslateT("Required modules");
				tviiT.itemex.state = tviiT.itemex.stateMask = TVIS_EXPANDED;
				if(bImagesLoaded) {
					tviiT.itemex.mask |= TVIF_IMAGE|TVIF_SELECTEDIMAGE;
					tviiT.itemex.iImage = tviiT.itemex.iSelectedImage = 0;
				}
				if(hItemEssential = TreeView_InsertItem(hTree, &tviiT)) {
					// disable state images
					TreeView_SetItemState(hTree, hItemEssential, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
					// insert essential items
					ExportTree_AddItem(hTree, hItemEssential, USERINFO, bImagesLoaded, 0);
					ExportTree_AddItem(hTree, hItemEssential, MOD_MBIRTHDAY, bImagesLoaded, 0);
					
					// protocol module is ignored for owner contact
					if(pDat->hContact != NULL) {
						ExportTree_AddItem(hTree, hItemEssential, "Protocol", bImagesLoaded, 0);
					}

					// base protocol is only valid for single exported contact at this position
					if(pszBaseProto) {
						ExportTree_AddItem(hTree, hItemEssential, pszBaseProto, bImagesLoaded, 0);
					}
				}

				// add items that are optional
				tviiT.hInsertAfter = TVI_LAST;
				tviiT.itemex.pszText = TranslateT("Optional modules");
				if(hItemOptional = TreeView_InsertItem(hTree, &tviiT)) {
					TreeView_SetItemState(hTree, hItemOptional, INDEXTOSTATEIMAGEMASK(0), TVIS_STATEIMAGEMASK);
					if(DBEnumModules(&Modules)) {
						for(pModule = Modules.first; pModule != NULL; pModule = pModule->next) {
							if(!pModule->pszName || *pModule->pszName == 0) continue;
							
							// module must exist in at least one contact
							if(pDat->hContact == INVALID_HANDLE_VALUE) {
								HANDLE hContact;
								
								for(hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0);
									hContact && hContact != INVALID_HANDLE_VALUE;
									hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0))
								{
									// ignore empty modules
									if(IsModuleEmpty(hContact, pModule->pszName))
										continue;
									
									// contact's base protocol is to be added to the treeview uniquely
									if(
										(pszBaseProto = (LPSTR)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact, NULL)) &&
										!mir_stricmp(pModule->pszName, pszBaseProto)
									  )
									{
										if(!ExportTree_FindItem(hTree, hItemEssential, (LPSTR)pModule->pszName))
											ExportTree_AddItem(hTree, hItemEssential, (LPSTR)pModule->pszName, bImagesLoaded, 0);
										continue;
									}

									// add optional module, which is valid for at least one contact
									if(
										mir_stricmp(pModule->pszName, USERINFO) &&
										mir_stricmp(pModule->pszName, MOD_MBIRTHDAY) &&
										mir_stricmp(pModule->pszName, "Protocol")
									  ) 
									{
										ExportTree_AddItem(hTree, hItemOptional, (LPSTR)pModule->pszName, bImagesLoaded, 1);
										break;
									}
								}
							}
							// module must exist in the selected contact
							else if(
								!IsModuleEmpty(pDat->hContact, pModule->pszName) &&
								mir_stricmp(pModule->pszName, USERINFO) &&
								mir_stricmp(pModule->pszName, MOD_MBIRTHDAY) &&
								(
									!pDat->hContact ||
									(
										mir_stricmp(pModule->pszName, "Protocol") &&
										mir_stricmp(pModule->pszName, pszBaseProto)
									)
								)
							  ) 
							{
								ExportTree_AddItem(hTree, hItemOptional, (LPSTR)pModule->pszName, bImagesLoaded, 1);
							}
						}
						DBFreeEnumList(&Modules);
					}
				}
			}
			return TRUE;
		}
		case WM_CTLCOLORSTATIC:
			if(GetDlgItem(hDlg, STATIC_WHITERECT) == (HWND)lParam || GetDlgItem(hDlg, ICO_DLGLOGO) == (HWND)lParam) {
				SetBkColor((HDC)wParam, RGB(255, 255, 255));
				return (BOOL)GetStockObject(WHITE_BRUSH);
			}
			SetBkMode((HDC)wParam, TRANSPARENT);
			return (BOOL)GetStockObject(NULL_BRUSH);

		case WM_COMMAND:
			switch(LOWORD(wParam)) {
				case IDOK:
				{
					HWND hTree = GetDlgItem(hDlg, IDC_TREE);
					HTREEITEM hParent;

					// search the tree item of optional items
					for(hParent = TreeView_GetRoot(hTree);
						hParent != NULL;
						hParent = TreeView_GetNextSibling(hTree, hParent))
					{
						ExportTree_AppendModuleList(hTree, hParent, pDat->pModules);
					}
					return EndDialog(hDlg, IDOK);
				}
				case IDCANCEL:
					return EndDialog(hDlg, IDCANCEL);

				case BTN_CHECK:
				case BTN_UNCHECK:
				{
					HWND hTree = GetDlgItem(hDlg, IDC_TREE);
					LPCSTR pszRoot = Translate("Optional modules");
					TVITEMA tvi;
					CHAR szText[128];

					tvi.mask = TVIF_TEXT;
					tvi.pszText = szText;
					tvi.cchTextMax = sizeof(szText);

					// search the tree item of optional items
					for(tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_ROOT, NULL);
						tvi.hItem != NULL && SendMessageA(hTree, TVM_GETITEMA, 0, (LPARAM)&tvi);
						tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)tvi.hItem))
					{
						if(!mir_stricmp(tvi.pszText, pszRoot)) {
							tvi.mask = TVIF_STATE;
							tvi.state = INDEXTOSTATEIMAGEMASK(LOWORD(wParam) == BTN_UNCHECK ? 1 : 2);
							tvi.stateMask = TVIS_STATEIMAGEMASK;

							for(tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_CHILD, (LPARAM)tvi.hItem);
								tvi.hItem != NULL;
								tvi.hItem = (HTREEITEM)SendMessageA(hTree, TVM_GETNEXTITEM, TVGN_NEXT, (LPARAM)tvi.hItem))
							{
								SendMessageA(hTree, TVM_SETITEMA, NULL, (LPARAM)&tvi);
							}
							break;
						}
					}
					break;
				}
			}
			break;

		case WM_DESTROY:
			FREE(pDat);
			break;
	}
	return 0;
}

/**
 * name:	export_GetModList
 * desc:	calls a dialog box that lists all modules for a specific contact
 * param:	hContact	- contact to list the modules for (-1 -> list all modules of all contacts)
 *			pModules	- pointer to an ENUMLIST structure that retrieves the resulting list of modules
 *			hParent		- handle to a window which should act as the parent of the created dialog
 * return:	0 if user pressed ok, 1 on cancel
 **/
INT export_GetModList(HANDLE hContact, LPENUMLIST pModules, HWND hParent)
{
	EXPORTDATA dat;

	dat.hContact = hContact;
	dat.pModules = pModules;
	return (IDOK != DialogBoxParam(ghInst, MAKEINTRESOURCE(IDD_EXPORT), hParent, export_GetModList_DlgProc, (LPARAM)&dat));
}